package com.ls.config.redis;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * redis工具类支持哨兵集群，和单机版
 * </p>
 *
 * @author TangQuanBin
 * @since 2019/10/11 19:28
 */
@SuppressWarnings("unchecked")
@Slf4j
@Component
public class RedisClient {

    @Resource(name = "redisTemplate")
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 公司前缀+id 对应 公司信息
     */
    public static final String COMPANY_PREFIX = "COMPANY";


    /**
     * 是否已经存在某个key
     *
     * @param key key
     * @return true-存在，false-不存在
     */
    public boolean existsKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Redis检查KEY异常{}", e.getMessage());
            return false;
        }
    }

    /**
     * 设置超时时间
     *
     * @param key    key
     * @param expire 超时时间，单位 秒
     * @return true-成功，false-失败
     */
    public boolean setExpireTime(String key, int expire) {
        try {
            if (expire > 0) {
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Redis设置超时时间异常{}", e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 设置今天晚上过期
     *
     * @param key
     * @return
     */
    public boolean setExpireTimeByToDay(String key) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR, 23);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.MILLISECOND, 999);
        int expire = (int) ((cal.getTimeInMillis() - System.currentTimeMillis()) / 1000);
        return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    /**
     * 设置月底过期
     *
     * @param key
     * @return
     */
    public boolean setExpireTimeByMonth(String key) {
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        cal.set(year, month + 1, 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) - 1);
        cal.set(Calendar.HOUR, 23);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.MILLISECOND, 999);
        int expire = (int) ((cal.getTimeInMillis() - System.currentTimeMillis()) / 1000);
        return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    /**
     * add single redis cache
     *
     * @param key   redis key
     * @param field redis field
     * @param value redis value
     * @return if add success
     */
    public boolean save(String key, String field, Object value) {
        String valueJson = null;
        try {
            valueJson = JSONObject.toJSONString(value);
            redisTemplate.opsForHash().put(key, field, valueJson);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存Redis数据异常，key={},field={},valueJson={},e={}", key, field, valueJson, e.getMessage());
            return false;
        }
        return true;
    }

    public boolean saveList(String key, List list) {
        try {
            redisTemplate.opsForList().rightPushAll(key, list);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存Redis数据异常，key={},field={},e={}", key, e.getMessage());
            return false;
        }
        return true;
    }
    public List getList(String key) {
        try {
            List resultList = redisTemplate.opsForList().range(key, 0, -1);
            return resultList;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存Redis数据异常，key={},field={},e={}", key, e.getMessage());
            return null;
        }
    }

    /**
     * add single redis cache
     *
     * @param key       redis key
     * @param field     redis field
     * @param valueJson redis value in json format
     * @return if add success
     */
    public boolean hashSave(String key, String field, String valueJson) {
        try {
            redisTemplate.opsForHash().put(key, field, valueJson);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存Redis数据异常，key={},field={},valueJson ={},e={}", key, field, valueJson, e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 保存key-value
     *
     * @param key   key
     * @param value value
     * @return 是否成功
     */
    public boolean save(String key, String value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存Redis数据异常，key={},value ={},e={}", key, value, e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 保存key-value
     *
     * @param key   key
     * @param value value
     * @return 是否成功
     */
    public boolean save(String key, String value, int expire) {
        try {
            redisTemplate.opsForValue().set(key, value);
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("保存Redis数据异常，key={},value ={},e={}", key, value, e.getMessage());
            return false;
        }
        return true;
    }

    public boolean addToZSet(String key, String value, double score) {
        try {
            redisTemplate.opsForZSet().add(key, value, score);
            return true;
        } catch (Exception e) {
            log.error("add to redis zSet error", e);
            return false;
        }
    }

    public Set<Object> getFromZSetByRank(String key, long minRank, long maxRank) {
        try {
            Set<Object> set = redisTemplate.opsForZSet().range(key, minRank, maxRank);
            return set;
        } catch (Exception e) {
            log.error("get from redis zSet error", e);
            return Collections.EMPTY_SET;
        }
    }

    public Set<ZSetOperations.TypedTuple<Object>> getFromZSetByRankWithScore(String key, long minRank, long maxRank) {
        try {
            return redisTemplate.opsForZSet().rangeWithScores(key, minRank, maxRank);
        } catch (Exception e) {
            log.error("get from redis zSet error", e);
            return Collections.EMPTY_SET;
        }
    }

    public long removeZSetByScore(String key, long min, long max) {
        try {
            return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
        } catch (Exception e) {
            log.error("get from redis zSet error", e);
            return 0;
        }
    }

    public long removeZSetMember(String key, String value) {
        try {
            return redisTemplate.opsForZSet().remove(key, value);
        } catch (Exception e) {
            log.error("remove from redis zSet error", e);
            return 0;
        }
    }

    public long getHashLen(String key) {
        try {
            return redisTemplate.opsForHash().size(key);
        } catch (Exception e) {
            log.error("get hash len error", e);
            return 0;
        }
    }

    public long getZSetSize(String key) {
        try {
            return redisTemplate.opsForZSet().size(key);
        } catch (Exception e) {
            log.error("get zset size error", e);
            return 0;
        }
    }

    /**
     * 查询数据
     *
     * @param key 查询key
     * @return
     */
    public String getJsonString(String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        return null == obj ? null:obj.toString();
    }


    /**
     * get all cached data in given redis key
     *
     * @param key redis key
     * @return all cached data,key is redis field
     */
    public Map<Object, Object> getAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取key-value的直接value，针对直接存储key-value的数据
     *
     * @param key   key
     * @param clazz value的类
     * @param <T>   value的类
     * @return 转化成Java对象的value值
     */
    public <T> T getValue(String key, Class<T> clazz) {
        Object value = redisTemplate.opsForValue().get(key);
        if (value == null) {
            return null;
        }
        JSONObject json = JSONObject.parseObject(redisTemplate.opsForValue().get(key).toString());
        return JSONObject.toJavaObject(json, clazz);
    }

    public int getIntValue(String key) {
        Object value = redisTemplate.opsForValue().get(key);
        if (value == null) {
            return 0;
        }
        return Integer.parseInt(value.toString());
    }


    /**
     * get a redis cache
     *
     * @param key   redis key
     * @param field redis field
     * @return redis value in json format
     */
    public String hget(String key, String field) {
        return (String) redisTemplate.opsForHash().get(key, field);
    }

    /**
     * get a redis cache
     *
     * @param key   redis key
     * @param field redis field
     * @param clazz redis value type
     * @return redis cache value
     */
    public <T> T hget(String key, String field, Class<T> clazz) {
        String jsonData = (String) redisTemplate.opsForHash().get(key, field);
        if (StringUtils.isNotBlank(jsonData)) {
            JSONObject jsonCacheObj = JSONObject.parseObject(jsonData);
            return JSONObject.toJavaObject(jsonCacheObj, clazz);
        }
        return null;
    }


    /**
     * get a redis cache
     *
     * @param key   redis key
     * @param field redis field
     * @param clazz redis value type
     * @return redis cache value
     * @author pengyu
     */
    public List getList(String key, String field, Class clazz) {
        String jsonData = (String) redisTemplate.opsForHash().get(key, field);
        if (StringUtils.isNotBlank(jsonData)) {
            JSONArray jsonArray = JSONArray.parseArray(jsonData);
            return JSONObject.parseArray(jsonArray.toJSONString(), clazz);
        }
        return null;


    }

    /**
     * delete all redis cache in given key
     *
     * @param key redis key
     * @return if delete success
     */
    public boolean delete(String key) {
        return redisTemplate.delete(key);
    }


    /**
     * delete redis cache in given key and fields
     *
     * @param key    redis key
     * @param fields redis fields
     * @return if delete success
     */
    public boolean deleteByHdel(String key, String... fields) {
        try {
            Long result0 = redisTemplate.opsForHash().delete(key, fields);
            log.info("deleteByHdel key,fields,{},{},{}",key ,fields, result0);
//            String field1 = "\""+fields+"\"";
//            Long result1 = redisTemplate.opsForHash().delete(key, field1);
//            log.info("deleteByHdel key,field1 {},{},{}",key ,field1, result1);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 增加有序集合
     *
     * @param key
     * @param value
     * @param seqNo
     * @return
     */
    public Boolean addZset(String key, Object value, double seqNo) {
        try {
            return redisTemplate.opsForZSet().add(key, value, seqNo);
        } catch (Exception e) {
            log.error("[RedisUtils.addZset] [error]", e);
            return false;
        }
    }

    /**
     * 获取zset集合数量
     * key不存在，返回0；存在，返回集合的个数。
     * @param key
     * @return
     */
    public Long countZset(String key) {
        try {
            return redisTemplate.opsForZSet().size(key);
        } catch (Exception e) {
            log.error("[RedisUtils.countZset] [error] [key is {}]", key, e);
            return 0L;
        }
    }

    /**
     * 获取zset指定范围内的集合
     * key存在，返回集合的对应位置的元素，区间左开右闭。start从0开始，end传-1表示查询所有。
     * System.out.println(redisUtils.rangeZset("person", 0, -1));
     * [1287989866203250690, 1287989874193399810, 1287989870372388866, 1287989878043770883, 1287989956804411394]
     * System.out.println(redisUtils.rangeZset("person", 0, 2));
     * [1287989866203250690, 1287989874193399810, 1287989870372388866]
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> rangeZset(String key, long start, long end) {
        try {
            return redisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception e) {
            log.error("[RedisUtils.rangeZset] [error] [key is {},start is {},end is {}]", key, start, end, e);
            return null;
        }
    }

    /**
     * 根据key和value移除指定元素
     * 未查询到对应的key和value，返回0，否则返回1
     * @param key
     * @param value
     * @return
     */
    public Long removeZset(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * 获取对应key和value的score
     *
     * @param key
     * @param value
     * @return
     */
    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 指定范围内元素排序
     * 到时候要看看是从小到大 还是从大到小  ，我才行是从小到大
     * @param key
     * @param v1
     * @param v2
     * @return
     */
    public Set<Object> rangeByScore(String key, double v1, double v2) {
        return redisTemplate.opsForZSet().rangeByScore(key, v1, v2);
    }

    /**
     * 指定元素增加指定值
     *
     * @param key
     * @param obj
     * @param score
     * @return
     */
    public Object addScore(String key, Object obj, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, obj, score);
    }

    /**
     * 排名
     * key和value未查询到对应的元素，返回null；存在，则直接返回元素对应的位置，从0开始，返回0，表示在第一位。
     * System.out.println(redisUtils.rank("person", 1287989866203250690L));
     * # 结果
     * 0
     * @param key
     * @param obj
     * @return
     */
    public Object rank(String key, Object obj) {
        return redisTemplate.opsForZSet().rank(key, obj);
    }

}
